#include "fxcc/core/graph/directX11/Shader.h"
#include "fxcc/core/graph/directX11/RenderStates.h"

using namespace fxcc::graph::directX11;

Shader::Shader(const std::string& vertCode, const std::string& fragCode, const fxcc::graph::common::VertexType
	& vertexType)
{
	m_Desc.m_FragCode = fragCode;
	m_Desc.m_VertCode = vertCode;
	m_Desc.m_VertexType = vertexType;

	if (!Init()) {
		ztclog::info("Failed create shader");
	}
};

bool Shader::Init()
{

	auto device = fxcc::graph::directX11::RenderStates::Inst().Device();

	ComPtr<ID3DBlob> vertexShaderBlob;

	HRESULT hr = CompileShaderFromString(m_Desc.m_VertCode.c_str(), "main", "vs_5_0", vertexShaderBlob);

	if (SUCCEEDED(hr))
	{
		hr = device->CreateVertexShader(vertexShaderBlob->GetBufferPointer(), vertexShaderBlob->GetBufferSize(), nullptr, m_VertexShader.GetAddressOf());
	}
	else
	{
		ztclog::info("failed create vertexShader ");
		return false;
	}

	{
		const auto& vertDesc = directX11::RenderStates::Inst().GetVertexDesc(m_Desc.m_VertexType);
		const auto& layout = vertDesc.m_InputLayouts;

		size_t bytecodeSize = vertexShaderBlob->GetBufferSize(); 
		HRESULT hr = device->CreateInputLayout(&layout[0], layout.size(), vertexShaderBlob->GetBufferPointer(), bytecodeSize, m_InputLayout.GetAddressOf());
		if (FAILED(hr))
		{
			ztclog::info("failed create input layout");
			return false;
		}
	}
	ComPtr<ID3DBlob> pixelShaderBlob;
	hr = CompileShaderFromString(m_Desc.m_FragCode.c_str(), "main", "ps_5_0", pixelShaderBlob);
	if (SUCCEEDED(hr))
	{
		hr = device->CreatePixelShader(pixelShaderBlob->GetBufferPointer(), pixelShaderBlob->GetBufferSize(), nullptr, m_PixelShader.GetAddressOf());
	}
	else
	{
		ztclog::info("failed create pixel shader");
		return false;
	}
	return true;
}

void fxcc::graph::directX11::Shader::Use()
{
	auto context = directX11::RenderStates::Inst().Context();

	context->VSSetShader(m_VertexShader.Get(), nullptr, 0);
	context->IASetInputLayout(m_InputLayout.Get());
	context->PSSetShader(m_PixelShader.Get(), nullptr, 0);
}


HRESULT fxcc::graph::directX11::Shader::CompileShaderFromString(const char* source, const std::string& entry, const std::string& shaderModel, ComPtr<ID3DBlob>& blob)
{
	UINT compileFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#ifdef _DEBUG
	compileFlags |= D3DCOMPILE_DEBUG;
#endif

	ID3DBlob* errorBlob = nullptr; 
	HRESULT hr = D3DCompile(source, strlen(source), nullptr, nullptr, nullptr,
		entry.c_str(), shaderModel.c_str(), compileFlags, 0,
		&blob, &errorBlob); 

	if (FAILED(hr))
	{
		if (errorBlob)
		{
			ztclog::info("Shader compilation failed: %s", static_cast<char*>(errorBlob->GetBufferPointer()));
			errorBlob->Release(); 
		}
		return hr; 
	}

	if (errorBlob) errorBlob->Release(); 

	return S_OK;
}

Shader::Shader(const ShaderDesc& desc):m_Desc(desc)
{
	if (!Init())
	{
		ztclog::info("failed create directX11 shader from desc");
	}
};

std::string fxcc::graph::directX11::Shader::testFrag()
{
	return R"(

Texture2D m_Texture : register(t0); 
SamplerState m_Texture2dSampler : register(s0); 
SamplerState m_Texture2dArraySampler : register(s1); 
SamplerState m_TextureCubeSampler : register(s2); 

struct FXCC_API PSInput
{
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD0;
};

float4 main(PSInput input) : SV_Target
{
    float4 color = m_Texture.Sample(m_Texture2dSampler, input.tex);
	return color;
}
)";

}

std::string fxcc::graph::directX11::Shader::testVert()
{
	return  R"(

struct FXCC_API VSInput
{
    float3 pos : POSITION;
    float3 normal : NORMAL;
    float2 tex : TEXCOORD;
    float3 tangent : TANGENT;
    float3 bigTangent : BIGTANGENT;
};

struct FXCC_API PSInput
{
    float4 pos : SV_POSITION;
    float2 tex : TEXCOORD0;
};

cbuffer PassDataBuffer : register(b0) 
{
    float4x4 proj;           
    float4x4 view;           
    float4x4 projView;       
    float3 viewPos;          
    float passPad;               
};

cbuffer ObjDataBuffer : register(b1) 
{
    float4x4 world;                  
    float4x4 worldInv;               
    float4x4 worldInvTranspose;      
    float3 objPad;                      
    float borderNormalAxis; 
         
};

PSInput main(VSInput input)
{
    PSInput output;
	float4 worldPosition = float4(input.pos, 1.0f);
    output.pos = mul(proj, mul(view, mul(world, worldPosition))); // proj * view * world * position
  	output.tex = input.tex;
    return output;
}
)";

}